package model

import (
	"gopkg.in/mgo.v2/bson"
	"src/config"
	newtime "src/util/time"
)

type Comment struct {
	ID                 bson.ObjectId `bson:"_id,omitempty" json:"id,omitempty"`
	Level              int           `bson:"level" json:"level"`
	Status             int           `bson:"status" json:"status"`
	Anonymity          bool          `bson:"anonymity" json:"anonymity"`
	Tags               []int         `bson:"tags" json:"tags"`
	ForumpostId        string        `bson:"forumpost_id" json:"forumpost_id"`
	ForumposterId      string        `bson:"forumposter_id" json:"forumposter_id"`
	ForumpostAnonymity bool          `bson:"forumpost_anonymity" json:"forumpost_anonymity"`
	UserID             string        `bson:"user_id" json:"user_id"`
	IssueTime          int64         `bson:"issue_time" json:"issue_time"`
	Text               string        `bson:"text" json:"text"`
	Imgs               []Img         `bson:"imgs" json:"imgs"`
	LikedUsers         []string      `bson:"liked_users" json:"liked_users"`
	Islike             bool          `bson:"islike" json:"islike"`
	Userinfo           User          `bson:"userinfo" json:"userinfo"`
	LikedUserNum       int           `bson:"liked_user_num" json:"liked_user_num"`
	Score              int           `bson:"score" json:"score"`
	ReplyNum           int           `bson:"reply_num" json:"reply_num"`
}

func CreateComment(comment Comment) (Comment, error) {
	query := bson.M{
		"_id":    bson.ObjectIdHex(comment.ForumpostId),
		"status": config.ForumPostCommonStatus,
	}
	f, err := findForumPost(query, DefaultSelector)
	if err != nil {
		return Comment{}, err
	}
	comment.ForumposterId = f.UserID
	comment.ForumpostAnonymity = f.Anonymity
	comment.IssueTime = newtime.GetMilliTimestamp()
	comment.Score = 0
	for i := 0; i < len(comment.Tags); i++ {
		comment.Score += config.TagsScore[comment.Tags[i]]
	}
	comment.Status = config.CommentCommonStatus
	comment.LikedUserNum = 0
	comment.ReplyNum = 0
	comment.LikedUsers = nil
	comment.Level = 1
	err = insertComment(comment)
	if err != nil {
		return Comment{}, err
	}
	return findComment(comment, DefaultSelector)
}

func DeleteComment(id, userid string, admin bool) (Comment, error) {
	if !bson.IsObjectIdHex(id) {
		return Comment{}, config.ErrorIDFormatWrong
	}
	var query bson.M
	if admin {
		query = bson.M{
			"_id": bson.ObjectIdHex(id),
		}
	} else {
		query = bson.M{
			"_id":     bson.ObjectIdHex(id),
			"user_id": userid,
		}
	}
	update := bson.M{
		"$set": bson.M{
			"status": config.CommentDeleteStatus,
		},
	}
	err := updateComment(query, update)
	if err != nil {
		return Comment{}, err
	}
	newquery := bson.M{
		"_id": bson.ObjectIdHex(id),
	}
	return findComment(newquery, DefaultSelector)
}

func GetCommentByForumpost(id string, page int, per_page int, user_id string) ([]Comment, error) {
	if !bson.IsObjectIdHex(id) {
		return []Comment{}, config.ErrorIDFormatWrong
	}
	query := bson.M{
		"forumpost_id": id,
		"status":       config.CommentCommonStatus,
	}
	fields := []string{
		"issue_time",
	}
	comment, err := findComments(query, DefaultSelector, page, per_page, fields...)
	for i := 0; i < len(comment); i++ {
		comment[i].Islike = false
		if user_id != "" {
			for _, s := range comment[i].LikedUsers {
				if s == user_id {
					comment[i].Islike = true
					break
				}
			}
		}
		if comment[i].ForumpostAnonymity {
			comment[i].ForumposterId = ""
		}
		if comment[i].Anonymity && user_id != comment[i].UserID {
			comment[i].UserID = ""
		} else {
			comment[i].Userinfo, err = GetUserInfo(comment[i].UserID)
		}
	}
	return comment, err
}

func GetCommentByForumpostALL(id string) ([]Comment, error) {
	if !bson.IsObjectIdHex(id) {
		return []Comment{}, config.ErrorIDFormatWrong
	}
	query := bson.M{
		"forumpost_id": id,
		"status":       config.CommentCommonStatus,
	}
	return findCommentsALL(query)
}

func GetCommentByid(id string, user_id string) (Comment, error) {
	if !bson.IsObjectIdHex(id) {
		return Comment{}, config.ErrorIDFormatWrong
	}
	query := bson.M{
		"_id":    bson.ObjectIdHex(id),
		"status": config.CommentCommonStatus,
	}
	comment, err := findComment(query, DefaultSelector)
	if err != nil {
		return Comment{}, err
	}
	comment.Islike = false
	if user_id != "" {
		for _, s := range comment.LikedUsers {
			if s == user_id {
				comment.Islike = true
				break
			}
		}
	}
	if comment.ForumpostAnonymity {
		comment.ForumposterId = ""
	}
	if comment.Anonymity && user_id != comment.UserID {
		comment.UserID = ""
	} else {
		comment.Userinfo, err = GetUserInfo(comment.UserID)
	}
	return comment, err
}

func GetCommentBykey_score(key string, tags int, page, perpage int, id string) ([]Comment, error) {
	var query bson.M
	if tags == 0 {
		query = bson.M{
			"text": bson.M{
				"$regex": bson.RegEx{
					Pattern: key,
					Options: "i",
				},
			},
			"status": config.ForumPostCommonStatus,
		}
	} else {
		query = bson.M{
			"tags": tags,
			"text": bson.M{
				"$regex": bson.RegEx{
					Pattern: key,
					Options: "i",
				},
			},
			"status": config.ForumPostCommonStatus,
		}
	}
	fields := []string{
		"-score", "-issue_time",
	}
	comments, err := findComments(query, DefaultSelector, page, perpage, fields...)
	for i := 0; i < len(comments); i++ {
		comments[i].Islike = false
		if id != "" {
			for _, s := range comments[i].LikedUsers {
				if s == id {
					comments[i].Islike = true
					break
				}
			}
		}
		if comments[i].ForumpostAnonymity {
			comments[i].ForumposterId = ""
		}
		if comments[i].Anonymity == true && id != comments[i].UserID {
			comments[i].UserID = ""
		} else {
			comments[i].Userinfo, err = GetUserInfo(comments[i].UserID)
		}
	}
	return comments, err
}

func UpdateCommentReply(id string, value int) error {
	if !bson.IsObjectIdHex(id) {
		return config.ErrorIDFormatWrong
	}
	updateQuery := bson.M{
		"_id": bson.ObjectIdHex(id),
	}
	update := bson.M{
		"$inc": bson.M{
			"reply_num": value,
		},
	}
	return CommentAction2(updateQuery, updateQuery, update)
}
func UpdateCommentScore(id string, value int) error {
	if !bson.IsObjectIdHex(id) {
		return config.ErrorIDFormatWrong
	}
	updateQuery := bson.M{
		"_id": bson.ObjectIdHex(id),
	}
	update := bson.M{
		"$inc": bson.M{
			"score": value,
		},
	}
	return CommentAction2(updateQuery, updateQuery, update)
}

func StarComment(id, user_id string, flag bool) error {
	if flag {
		return likeComment(id, user_id)
	}
	return cancelLikeComment(id, user_id)
}
func likeComment(id, user_id string) error {
	if !bson.IsObjectIdHex(id) {
		return config.ErrorIDFormatWrong
	}

	findQuery := bson.M{
		"_id":         bson.ObjectIdHex(id),
		"liked_users": user_id,
	}
	updateQuery := bson.M{
		"_id": bson.ObjectIdHex(id),
	}
	update := bson.M{
		"$inc": bson.M{
			"liked_user_num": 1,
		},
		"$push": bson.M{
			"liked_users": user_id,
		},
	}

	return commentAction(findQuery, updateQuery, update)
}
func cancelLikeComment(id, user_id string) error {
	if !bson.IsObjectIdHex(id) {
		return config.ErrorIDFormatWrong
	}

	findQuery := bson.M{
		"_id":         bson.ObjectIdHex(id),
		"liked_users": user_id,
	}
	updateQuery := bson.M{
		"_id": bson.ObjectIdHex(id),
	}
	update := bson.M{
		"$inc": bson.M{
			"liked_user_num": -1,
		},
		"$pull": bson.M{
			"liked_users": user_id,
		},
	}
	return CommentAction2(findQuery, updateQuery, update)
}

func commentAction(findQuery, updateQuery, update interface{}) error {
	sess := globalSess.Clone()
	defer sess.Close()
	commentTable := sess.DB(DBNAME).C(COMMENTTABLE)

	var comment Comment
	err := commentTable.Find(findQuery).One(&comment)
	if err == nil {
		return config.ErrorHasExist
	}

	return commentTable.Update(updateQuery, update)
}
func CommentAction2(findQuery, updateQuery, update interface{}) error {
	sess := globalSess.Clone()
	defer sess.Close()
	commentTable := sess.DB(DBNAME).C(COMMENTTABLE)

	var comment Comment
	err := commentTable.Find(findQuery).One(&comment)
	if err != nil {
		return err
	}

	return commentTable.Update(updateQuery, update)
}

/*************/
func updateComment(query, update interface{}) error {
	sess := globalSess.Clone()
	defer sess.Close()
	commentTable := sess.DB(DBNAME).C(COMMENTTABLE)

	return commentTable.Update(query, update)
}
func insertComment(docs ...interface{}) error {
	sess := globalSess.Clone()
	defer sess.Close()
	commentTable := sess.DB(DBNAME).C(COMMENTTABLE)

	return commentTable.Insert(docs...)
}
func findComment(query, selector interface{}) (Comment, error) {
	sess := globalSess.Copy()
	defer sess.Close()
	commentTable := sess.DB(DBNAME).C(COMMENTTABLE)

	comment := Comment{}
	err := commentTable.Find(query).Select(selector).One(&comment)
	return comment, err
}
func findCommentNum(query interface{}) (int, error) {
	sess := globalSess.Copy()
	defer sess.Close()
	commentTable := sess.DB(DBNAME).C(COMMENTTABLE)

	return commentTable.Find(query).Count()
}
func findComments(query, selector interface{}, page, perPage int, fields ...string) ([]Comment, error) {
	sess := globalSess.Copy()
	defer sess.Close()
	commentTable := sess.DB(DBNAME).C(COMMENTTABLE)

	comments := []Comment{}
	err := commentTable.Find(query).Select(selector).Sort(fields...).Skip((page - 1) * perPage).Limit(perPage).All(&comments)
	return comments, err
}
func findCommentsALL(query interface{}) ([]Comment, error) {
	sess := globalSess.Copy()
	defer sess.Close()
	commentTable := sess.DB(DBNAME).C(COMMENTTABLE)

	comments := []Comment{}
	err := commentTable.Find(query).All(&comments)
	return comments, err
}
